import json
import os
from utils.volunteerData_util import save_to_db  # 从 utils 包导入保存到数据库的函数
from typing import List, Set, Tuple
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
from crawl4ai import (
    AsyncWebCrawler,
    BrowserConfig,
    CacheMode,
    CrawlerRunConfig,
    LLMExtractionStrategy,
)


def get_browser_config() -> BrowserConfig:
    """
    返回爬虫的浏览器配置。

    Returns:
        BrowserConfig: 浏览器的配置设置。
    """
    # https://docs.crawl4ai.com/core/browser-crawler-config/
    browser_conf = BrowserConfig(
        browser_type="chromium",  # 使用Chromium浏览器
        headless=True,            # 设置为True以隐藏浏览器窗口
        viewport_width=1280,      # 设置窗口宽度
        viewport_height=720,      # 设置窗口高度
        user_agent_mode="random",  # 随机生成user-agent
        verbose=True,  # 启用详细日志记录
    )

    return browser_conf


def get_crawler_run_config(
        session_id: str,
) -> CrawlerRunConfig:
    """
    返回爬虫运行配置。
    Args:
        session_id (str): 会话标识符。
    Returns:
        CrawlerRunConfig: 爬虫运行的配置设置。
    """
    # https://docs.crawl4ai.com/core/crawler-run-config/
    crawl_conf = CrawlerRunConfig(
        cache_mode=CacheMode.BYPASS,  # 跳过缓存，获取最新内容
        wait_for="class:.cont_l .zycx",         # 等待表格加载完成
        screenshot=False,              # 不需要截图
        session_id=session_id         # 会话标识符
    )

    return crawl_conf


def get_extraction_strategy() -> JsonCssExtractionStrategy:
    """
    返回 JSON CSS 提取策略的配置。

    Returns:
        JsonCssExtractionStrategy: 如何使用 JSON CSS 提取数据的设置。
    """
    # 定义表格提取Schema
    schema = {
        "name": "College Data",
        "baseSelector": "tr.szw, tr.sz",  # 选择所有带有 class="szw" 或 class="sz" 的 <tr> 标签
        "fields": [
            {"name": "speciality", "selector": "td:nth-child(1) a", "type": "text"},  # 专业名称
            {"name": "school_name", "selector": "td:nth-child(2) a", "type": "text"},  # 学校名称
            {"name": "avg_score", "selector": "td.pjf a", "type": "text"},  # 平均分
            {"name": "max_score", "selector": "td:nth-child(4)", "type": "text"},  # 分数
            {"name": "province", "selector": "td:nth-child(5)", "type": "text"},  # 省份
            {"name": "category", "selector": "td:nth-child(6)", "type": "text"},  # 类别
            {"name": "year", "selector": "td:nth-child(7)", "type": "text"},  # 年份
            {"name": "batch", "selector": "td:nth-child(8)", "type": "text"},  # 批次
        ]
    }
    # 使用JsonCssExtractionStrategy提取表格数据
    extraction_strategy = JsonCssExtractionStrategy(schema)

    return extraction_strategy



# def get_llm_strategy() -> LLMExtractionStrategy:
#     """
#     返回语言模型提取策略的配置。
#
#     Returns:
#         LLMExtractionStrategy: 如何使用 LLM 提取数据的设置。
#     """
#     # https://docs.crawl4ai.com/api/strategies/#llmextractionstrategy
#     return LLMExtractionStrategy(
#         provider="groq/deepseek-r1-distill-llama-70b",  # LLM 提供程序的名称
#         api_token=os.getenv("GROQ_API_KEY"),  # 用于身份验证的 API 令牌
#         schema=Venue.model_json_schema(),  # 数据模型的 JSON 模式
#         extraction_type="schema",  # 要执行的提取类型，可以是 "block" 或 "schema"，schema 类型会自动将数据转换为模型类的实例
#         instruction=(
#             "Extract all venue objects with 'name', 'location', 'price', 'capacity', "
#             "'rating', 'reviews', and a 1 sentence description of the venue from the "
#             "following content."
#         ),  # LLM 说明
#         input_format="markdown",  # 输入内容的格式
#         verbose=True,  # 启用详细日志记录
#     )


async def check_no_results(
    cleaned_html: str,
    url: str,
) -> bool:
    """
    检查页面上是否存在 “抱歉，没有找到相关内容” 消息。

    Args:
        cleaned_html (str): 要检查的 HTML 内容。
        url (str): 要检查的 URL。

    Returns:
        bool: 如果找到 “抱歉，没有找到相关内容” 消息，则为 True，否则为 False。
    """
    if "抱歉，没有找到相关内容" in cleaned_html:
        print(f"页面上出现 “抱歉，没有找到相关内容” 消息。在 {url} 页面上没有找到相关内容。")
        return True
    return False


def save_error_to_file(file_path: str, error_message: str):
    """
    将错误信息保存到指定的文本文件中。

    Args:
        file_path (str): 错误日志文件路径。
        error_message (str): 要保存的错误信息。
    """
    try:
        with open(file_path, "a", encoding="utf-8") as f:
            f.write(error_message)
    except Exception as e:
        print(f"保存错误日志失败: {e}")


async def fetch_and_process_page(
    crawler: AsyncWebCrawler,
    page_number: int,
    province: int,
    base_url: str,
    session_id: str,
) -> Tuple[List[dict], bool]:
    """
    获取并处理单页的场地数据，并将报错信息保存到一个 txt 文件中。

    Args:
        crawler (AsyncWebCrawler): 网络爬虫实例。
        page_number (int): 要获取的页码。
        province (int): 省份编号。
        base_url (str): 网站的基本 URL。
        session_id (str): 会话标识符。

    Returns:
        Tuple[List[dict], bool]:
            - List[dict]: 页面中已处理场地的列表。
            - bool: 指示是否遇到 “No Results Found ”信息的标志。
    """
    url = f"{base_url}a{province}/p{page_number}"
    print(f"正在加载页面 {page_number}...{url}")

    # 定义错误日志文件路径
    error_log_file = "error_log.txt"

    try:
        # 使用提取策略获取页面内容
        result = await crawler.arun(
            url=url,
            config=get_crawler_run_config(session_id),
        )

        if not result.success:
            print(f"获取页面出错 {page_number}: {result.error_message}")
            save_error_to_file(error_log_file, f"获取页面出错 {page_number}: {result.error_message}\n")
            return [], False

        # 检查是否出现 “抱歉，没有找到相关内容 ”信息
        no_results = await check_no_results(result.cleaned_html, url)
        if no_results:
            return [], True  # 没有更多结果，发出停止爬行信号

        # 解析提取的内容
        try:
            extracted_data = get_extraction_strategy().extract(url=url, html_content=result.html)
        except Exception as e:
            print(f"页面 {page_number} 解析出错: {e}")
            save_error_to_file(error_log_file, f"省份{province}， 页面 {page_number} 解析出错: {e}\n")
            return [], False

        if not extracted_data:
            print(f"页面 {page_number} 上未找到高考专业分数线数据。")
            return [], False

        # 加工高考专业分数线数据
        complete_gaokao_data = []
        for item in extracted_data:
            # Debugging:
            print("高考专业分数线数据：", item)

            # 循环数据，如果数据中有“-”字符，则删除该数据
            for key in item:
                if "-" in item[key]:
                    item[key] = item[key].replace("-", "")
            save_to_db(item)
            complete_gaokao_data.append(item)

        if not complete_gaokao_data:
            print(f"页面上未找到完整的场馆数据 {page_number}.")
            return [], False

        print(f"提取到 {len(complete_gaokao_data)} 数据，{province} 省，第 {page_number} 页。")
        return complete_gaokao_data, False  # 继续爬行

    except Exception as e:
        # 捕获所有其他异常并记录到文件
        error_message = f"发生未知错误在省份{province}， 页面 {page_number} 解析出错: {e}\n"
        print(error_message)
        save_error_to_file(error_log_file, error_message)
        return [], False
